package hydrap2p.speedlistener;

import hydrap2p.ServiceLocation;
import hydrap2p.helper.ServiceConnector;
import hydrap2p.servicemanager.NameTaken;

import java.io.IOException;
import java.net.BindException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.ByteBuffer;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;

import org.apache.thrift.TException;
import org.apache.thrift.server.TServer;
import org.apache.thrift.server.TThreadPoolServer;
import org.apache.thrift.transport.TServerSocket;
import org.apache.thrift.transport.TServerTransport;

public class SpeedListenerMain implements SpeedListener.Iface {

	private static final long pingerdelay = 10000;
	private TServer server;
	private Timer timer;
	
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		try {
			new SpeedListenerMain().run(9651);
		} catch (IOException e) {
			System.err.println(e.getLocalizedMessage());
		}

	}
	
	private void run(int suggested) throws IOException
	{
		Random ports=new Random();
		int minport=1025;
		int maxport=50000;
		
		System.out.println("Starting server");
		ServerSocket socket = new ServerSocket();
		int port=suggested;
		int attempts=10;
		while (socket.getLocalPort()==-1 && attempts>0)
		{
			System.out.println("Trying to bind to port "+port);
			try {
				socket.bind(new InetSocketAddress("localhost",port));
			}
			catch (Exception e) {}
			if (socket.getLocalPort()==-1)
				port=ports.nextInt(maxport-minport)+minport;
			attempts--;
		}
		if (socket.getLocalPort()==-1)
			throw(new BindException("Could not find a port to bind"));
		
		ServiceLocation here=new ServiceLocation();
		here.address="localhost";
		here.port=port;
		try {
			ServiceConnector.getServiceManager().setServiceLocation("SpeedListener", here);
		} catch (TException e) {
			e.printStackTrace();
			throw(new BindException("Failed to tell locator about my location"));
		} catch (NameTaken e) {
			throw(new BindException("Some other component has already claimed my interface location: "+e.getLocation().address+":"+e.getLocation().port));
		} catch (NullPointerException e) {
			throw(new BindException("Failed to connect to locator"));
		}
		
		timer = new Timer();
		timer.schedule(new PingTimer(), pingerdelay, pingerdelay);
		
		
		TServerTransport tsocket = new TServerSocket(socket);
		TThreadPoolServer.Args args = new TThreadPoolServer.Args(tsocket);
		args.processor(new SpeedListener.Processor(this));
		server = new TThreadPoolServer(args);
		server.serve();
		System.out.println("Stopping");
	}


	private class PingTimer extends TimerTask {

		@Override
		public void run() {
			try {
				ServiceConnector.getServiceManager().ping();
			} catch (Exception e) {
				System.out.println("Could not ping Manager, disconnecting");
				shutdown();
			}
		}
	}
	
	private void shutdown()
	{

		try {
			ServiceConnector.getServiceManager().delServiceLocation("SpeedListener");
		} catch (Exception e) {
		}
		server.stop();
		timer.cancel();
		System.exit(0);
	}

	@Override
	public void ping() throws TException {
		//System.out.println("Received ping");
		return;	
	}

	@Override
	public void absorbBytes(int length, ByteBuffer test) throws TException {
		if (length==test.capacity())
			System.out.println("Received byte array of "+length);
		else
			System.err.println("Received byte array of "+test.capacity()+" but we expected "+length);
		return;
	}

	@Override
	public void absorbString(int length, String test) throws TException {
		if (length==test.length())
			System.out.println("Received string of "+length);
		else
			System.err.println("Received string of "+test.length()+" but we expected "+length);
		return;
	}

	@Override
	public String md5(int length, ByteBuffer test) throws TException {
		if (length==test.capacity())
			System.out.println("Received byte array of "+length);
		else
			System.err.println("Received byte array of "+test.capacity()+" but we expected "+length);
		
		MessageDigest m=null;
		try {
			m=MessageDigest.getInstance("MD5");

			m.update(test);
			return m.toString();
		} catch (NoSuchAlgorithmException e) {
			System.err.println("Failed to create MD5 object, how odd");
		}
		return null;
		
	}


	@Override
	public List<String> supportedInterfaces() throws TException {
		LinkedList<String> temp = new LinkedList<String>();
		temp.add("ModuleControl");
		temp.add("SpeedListener");
		return temp;
	}


	@Override
	public void stop() throws TException {
		System.out.println("Closing by thrift");
		shutdown();
	}


}
